/******************************************************************************
glsl.h
Version: 1.2.1
Last update: 2010/10/06 

-------------------------------------------------------------------------------
WARNING: This version breaks compatibility with earlier libglsl versions!
         This is mainly because of performance improvements.
-------------------------------------------------------------------------------

TODO: (1) remove <GL/glew.h> from header.
      (2) Change some class names (not starting with "gl").

           * remove  friend class glShaderManager; from glShader class 
           * glShaderManager -> ShaderHandler
           * glShader -> Shader
           * move VertexShader, FragmentShader and GeometryShader classes to .cpp as they are 
             not required and just confusing
           * glShaderObject -> private class

      (3) Change some method names

      (4) This class must work for OpenGL ES 2.x and OpenGL 2,3,4

(c) 2003-2010 by Martin Christen. All Rights reserved.
******************************************************************************/


#ifndef A_GLSL_H
#define A_GLSL_H

#include <vector>
#include <iostream>
#include <GL/glew.h>


   class glShaderManager;

   //! Shader Object holds the Shader Source, the OpenGL "Shader Object" and provides some methods to load shaders.
   /*!
         \author Martin Christen 
         \ingroup GLSL
         \bug This class should be an interface. (pure virtual) 	
   */
   class glShaderObject
   {
      friend class glShader;

   public:
                  glShaderObject();
      virtual     ~glShaderObject();
        
      int         load(const char* filename);               //!< \brief Loads a shader file. \param filename The name of the ASCII file containing the shader. \return returns 0 if everything is ok.\n -1: File not found\n -2: Empty File\n -3: no memory
      void        loadFromMemory(const char* program);      //!< \brief Load program from null-terminated char array. \param program Address of the memory containing the shader program.
       
      bool        compile(void);                            //!< compile program
      char*       getCompilerLog(void);                     //!< get compiler messages
      GLint       getAttribLocation(char* attribName);      //!< \brief Retrieve attribute location. \return Returns attribute location. \param attribName Specify attribute name.  
    
   protected:
       int        program_type;  //!< The program type. 1=Vertex Program, 2=Fragment Program, 3=Geometry Progam, 0=none

       GLuint     ShaderObject;  //!< Shader Object
       GLubyte*   ShaderSource;  //!< ASCII Source-Code
       
       GLcharARB* compiler_log;
       
       bool       is_compiled;   //!< true if compiled
       bool       _memalloc;     //!< true if memory for shader source was allocated
   };

//-----------------------------------------------------------------------------

   //! \brief Class holding Vertex Shader \ingroup GLSL \author Martin Christen
   class VertexShader : public glShaderObject
   {
   public:
                  VertexShader();  //!< Constructor for Vertex Shader
      virtual     ~VertexShader(); 
   };

//-----------------------------------------------------------------------------

   //! \brief Class holding Fragment Shader \ingroup GLSL \author Martin Christen
   class FragmentShader : public glShaderObject
   {
   public:
                  FragmentShader(); //!< Constructor for Fragment Shader
      virtual     ~FragmentShader();
    
   };

//-----------------------------------------------------------------------------

   //! \brief Class holding Geometry Shader \ingroup GLSL \author Martin Christen
   class GeometryShader : public glShaderObject
   {
   public:
                   GeometryShader(); //!< Constructor for Geometry Shader
      virtual     ~GeometryShader();
   };

//-----------------------------------------------------------------------------

   //! \brief Controlling compiled and linked GLSL program. \ingroup GLSL \author Martin Christen
   class glShader
   {
      friend class glShaderManager;

   public:
                 glShader();                  
      virtual    ~glShader();

      void       addShader(glShaderObject* ShaderProgram); //!< add a Vertex or Fragment Program \param ShaderProgram The shader object.
      
      //!< Returns the OpenGL Program Object (only needed if you want to control everything yourself) \return The OpenGL Program Object
      GLuint     GetProgramObject(){return ProgramObject;}

      bool       link(void);                        //!< Link all Shaders
      char*      getLinkerLog(void);                //!< Get Linker Messages \return char pointer to linker messages. Memory of this string is available until you link again or destroy this class.

      void       Bind();                           //!< bind Shader
      void       Unbind();                         //!< unbind shader
      
      // Geometry Shader: Input Type, Output and Number of Vertices out
      void       SetInputPrimitiveType(int nInputPrimitiveType);   //!< Set the input primitive type for the geometry shader
      void       SetOutputPrimitiveType(int nOutputPrimitiveType); //!< Set the output primitive type for the geometry shader
      void       SetVerticesOut(int nVerticesOut);                 //!< Set the maximal number of vertices the geometry shader can output
     
      // Get Uniform location
      GLint      GetUniformLocation(const GLcharARB *name);  //!< Retrieve Location (index) of a Uniform Variable. Only valid if shader is linked.

      // Submitting Uniform Variables. You can set varname to 0 and specifiy index retrieved with GetUniformLocation (best performance)
      void       setUniform1f(GLint uniformlocation, GLfloat v0);  //!< Specify value of uniform variable. \param varname The name of the uniform variable.
      void       setUniform2f(GLint uniformlocation, GLfloat v0, GLfloat v1);  //!< Specify value of uniform variable. \param varname The name of the uniform variable.
      void       setUniform3f(GLint uniformlocation, GLfloat v0, GLfloat v1, GLfloat v2);  //!< Specify value of uniform variable. \param varname The name of the uniform variable.
      void       setUniform4f(GLint uniformlocation, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);  //!< Specify value of uniform variable. \param varname The name of the uniform variable.

      void       setUniform1i(GLint uniformlocation, GLint v0);  //!< Specify value of uniform integer variable. \param varname The name of the uniform variable.
      void       setUniform2i(GLint uniformlocation, GLint v0, GLint v1); //!< Specify value of uniform integer variable. \param varname The name of the uniform variable.
      void       setUniform3i(GLint uniformlocation, GLint v0, GLint v1, GLint v2); //!< Specify value of uniform integer variable. \param varname The name of the uniform variable.
      void       setUniform4i(GLint uniformlocation, GLint v0, GLint v1, GLint v2, GLint v3); //!< Specify value of uniform integer variable. \param varname The name of the uniform variable.

      // Note: unsigned integers require GL_EXT_gpu_shader4 (for example GeForce 8800)
      void       setUniform1ui(GLint uniformlocation, GLuint v0); //!< Specify value of uniform unsigned integer variable. \warning Requires GL_EXT_gpu_shader4. \param varname The name of the uniform variable.
      void       setUniform2ui(GLint uniformlocation, GLuint v0, GLuint v1); //!< Specify value of uniform unsigned integer variable. \warning Requires GL_EXT_gpu_shader4. \param varname The name of the uniform variable.
      void       setUniform3ui(GLint uniformlocation, GLuint v0, GLuint v1, GLuint v2); //!< Specify value of uniform unsigned integer variable. \warning Requires GL_EXT_gpu_shader4. \param varname The name of the uniform variable.
      void       setUniform4ui(GLint uniformlocation, GLuint v0, GLuint v1, GLuint v2, GLuint v3); //!< Specify value of uniform unsigned integer variable. \warning Requires GL_EXT_gpu_shader4. \param varname The name of the uniform variable.

      // Arrays
      void       setUniform1fv(GLint uniformlocation, GLsizei count, GLfloat *value); //!< Specify values of uniform array. \param varname The name of the uniform variable.
      void       setUniform2fv(GLint uniformlocation, GLsizei count, GLfloat *value); //!< Specify values of uniform array. \param varname The name of the uniform variable.
      void       setUniform3fv(GLint uniformlocation, GLsizei count, GLfloat *value); //!< Specify values of uniform array. \param varname The name of the uniform variable.
      void       setUniform4fv(GLint uniformlocation, GLsizei count, GLfloat *value); //!< Specify values of uniform array. \param varname The name of the uniform variable.
      
      void       setUniform1iv(GLint uniformlocation, GLsizei count, GLint *value); //!< Specify values of uniform array. \param varname The name of the uniform variable.
      void       setUniform2iv(GLint uniformlocation, GLsizei count, GLint *value); //!< Specify values of uniform array. \param varname The name of the uniform variable.
      void       setUniform3iv(GLint uniformlocation, GLsizei count, GLint *value); //!< Specify values of uniform array. \param varname The name of the uniform variable.
      void       setUniform4iv(GLint uniformlocation, GLsizei count, GLint *value); //!< Specify values of uniform array. \param varname The name of the uniform variable.
      
      void       setUniform1uiv(GLint uniformlocation, GLsizei count, GLuint *value); //!< Specify values of uniform array. \warning Requires GL_EXT_gpu_shader4. \param varname The name of the uniform variable.
      void       setUniform2uiv(GLint uniformlocation, GLsizei count, GLuint *value); //!< Specify values of uniform array. \warning Requires GL_EXT_gpu_shader4. \param varname The name of the uniform variable.
      void       setUniform3uiv(GLint uniformlocation, GLsizei count, GLuint *value); //!< Specify values of uniform array. \warning Requires GL_EXT_gpu_shader4. \param varname The name of the uniform variable.
      void       setUniform4uiv(GLint uniformlocation, GLsizei count, GLuint *value); //!< Specify values of uniform array. \warning Requires GL_EXT_gpu_shader4. \param varname The name of the uniform variable.
      
      void       setUniformMatrix2fv(GLint uniformlocation, GLsizei count, GLboolean transpose, GLfloat *value); //!< Specify values of uniform 2x2 matrix. \param varname The name of the uniform variable.
      void       setUniformMatrix3fv(GLint uniformlocation, GLsizei count, GLboolean transpose, GLfloat *value); //!< Specify values of uniform 3x3 matrix. \param varname The name of the uniform variable.
      void       setUniformMatrix4fv(GLint uniformlocation, GLsizei count, GLboolean transpose, GLfloat *value); //!< Specify values of uniform 4x4 matrix. \param varname The name of the uniform variable.
 
      // Receive Uniform variables:
      void       getUniformfv(GLint uniformlocation, GLfloat* values); //!< Receive value of uniform variable. \param varname The name of the uniform variable.
      void       getUniformiv(GLint uniformlocation, GLint* values); //!< Receive value of uniform variable. \param varname The name of the uniform variable.
      void       getUniformuiv(GLint uniformlocation, GLuint* values); //!< Receive value of uniform variable. \warning Requires GL_EXT_gpu_shader4 \param varname The name of the uniform variable.

      void       BindAttribLocation(GLint index, GLchar* name); //! Bind Attribute location. Please note that you have to link application after changing attribute location 

      //GLfloat
      bool        setVertexAttrib1f(GLuint index, GLfloat v0); 				                         //!< Specify value of attribute. \param index The index of the vertex attribute. \param v0 value of the attribute component
      bool        setVertexAttrib2f(GLuint index, GLfloat v0, GLfloat v1); 								 //!< Specify value of attribute. \param index The index of the vertex attribute. \param v0 value of the attribute component \param v1 value of the attribute component
      bool        setVertexAttrib3f(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2);					 //!< Specify value of attribute. \param index The index of the vertex attribute. \param v0 value of the attribute component \param v1 value of the attribute component \param v2 value of the attribute component
      bool        setVertexAttrib4f(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);  //!< Specify value of attribute. \param index The index of the vertex attribute. \param v0 value of the attribute component \param v1 value of the attribute component \param v2 value of the attribute component \param v3 value of the attribute component

      //GLdouble
      bool        setVertexAttrib1d(GLuint index, GLdouble v0);                                         //!< Specify value of attribute. \param index The index of the vertex attribute. \param v0 value of the attribute component
      bool        setVertexAttrib2d(GLuint index, GLdouble v0, GLdouble v1);                            //!< Specify value of attribute. \param index The index of the vertex attribute. \param v0 value of the attribute component \param v1 value of the attribute component
      bool        setVertexAttrib3d(GLuint index, GLdouble v0, GLdouble v1, GLdouble v2);               //!< Specify value of attribute. \param index The index of the vertex attribute. \param v0 value of the attribute component \param v1 value of the attribute component \param v2 value of the attribute component
      bool        setVertexAttrib4d(GLuint index, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3);  //!< Specify value of attribute. \param index The index of the vertex attribute. \param v0 value of the attribute component \param v1 value of the attribute component \param v2 value of the attribute component \param v3 value of the attribute component

      //GLshort
      bool        setVertexAttrib1s(GLuint index, GLshort v0);                                      //!< Specify value of attribute. \param index The index of the vertex attribute. \param v0 value of the attribute component
      bool        setVertexAttrib2s(GLuint index, GLshort v0, GLshort v1);                          //!< Specify value of attribute. \param index The index of the vertex attribute. \param v0 value of the attribute component \param v1 value of the attribute component
      bool        setVertexAttrib3s(GLuint index, GLshort v0, GLshort v1, GLshort v2);              //!< Specify value of attribute. \param index The index of the vertex attribute. \param v0 value of the attribute component \param v1 value of the attribute component \param v2 value of the attribute component
      bool        setVertexAttrib4s(GLuint index, GLshort v0, GLshort v1, GLshort v2, GLshort v3);  //!< Specify value of attribute. \param index The index of the vertex attribute. \param v0 value of the attribute component \param v1 value of the attribute component \param v2 value of the attribute component \param v3 value of the attribute component

      // Normalized Byte (for example for RGBA colors)
      bool        setVertexAttribNormalizedByte(GLuint index, GLbyte v0, GLbyte v1, GLbyte v2, GLbyte v3); //!< Specify value of attribute. Values will be normalized.

      //GLint (Requires GL_EXT_gpu_shader4)
      bool        setVertexAttrib1i(GLuint index, GLint v0); //!< Specify value of attribute. Requires GL_EXT_gpu_shader4.
      bool        setVertexAttrib2i(GLuint index, GLint v0, GLint v1); //!< Specify value of attribute. Requires GL_EXT_gpu_shader4.
      bool        setVertexAttrib3i(GLuint index, GLint v0, GLint v1, GLint v2); //!< Specify value of attribute. Requires GL_EXT_gpu_shader4.
      bool        setVertexAttrib4i(GLuint index, GLint v0, GLint v1, GLint v2, GLint v3); //!< Specify value of attribute. Requires GL_EXT_gpu_shader4.

      //GLuint (Requires GL_EXT_gpu_shader4)
      bool        setVertexAttrib1ui(GLuint index, GLuint v0); //!< Specify value of attribute. \warning Requires GL_EXT_gpu_shader4. \param v0 value of the first component
      bool        setVertexAttrib2ui(GLuint index, GLuint v0, GLuint v1); //!< Specify value of attribute. \warning Requires GL_EXT_gpu_shader4.
      bool        setVertexAttrib3ui(GLuint index, GLuint v0, GLuint v1, GLuint v2); //!< Specify value of attribute. \warning Requires GL_EXT_gpu_shader4.
      bool        setVertexAttrib4ui(GLuint index, GLuint v0, GLuint v1, GLuint v2, GLuint v3); //!< Specify value of attribute. \warning Requires GL_EXT_gpu_shader4.
     
   protected:
      void        manageMemory(void){_mM = true;}
      void        UsesGeometryShader(bool bYesNo){ _bUsesGeometryShader = bYesNo;}

   private:      
      GLuint      ProgramObject;                      // GLProgramObject
      

      GLcharARB*  linker_log;
      bool        is_linked;
      std::vector<glShaderObject*> ShaderList;       // List of all Shader Programs

      bool        _mM;
      bool        _bUsesGeometryShader;

      int         _nInputPrimitiveType;
      int         _nOutputPrimitiveType;
      int         _nVerticesOut;
        
   };

//-----------------------------------------------------------------------------
	
	//! To simplify the process loading/compiling/linking shaders this high level interface to simplify setup of a vertex/fragment shader was created. \ingroup GLSL \author Martin Christen
   class glShaderManager
   {
   public:
       glShaderManager();
       virtual ~glShaderManager();

       // Vertex and Fragment shader
       glShader* loadfromFile(const char* vertexFile, const char* fragmentFile, bool bLink=true);    //!< load vertex/fragment shader from file. If you specify 0 for one of the shaders, the fixed function pipeline is used for that part. \param vertexFile Vertex Shader File. \param fragmentFile Fragment Shader File.
       glShader* loadfromMemory(const char* vertexMem, const char* fragmentMem, bool bLink=true); //!< load vertex/fragment shader from memory. If you specify 0 for one of the shaders, the fixed function pipeline is used for that part.
       
       // Vertex, Fragment and Geometry shader
       glShader* loadfromFile(const char* vertexFile, const char* geometryFile, const char* fragmentFile, bool bLink=true); //!< load vertex/geometry/fragment shader from file. If you specify 0 for one of the shaders, the fixed function pipeline is used for that part. \param vertexFile Vertex Shader File. \param geometryFile Geometry Shader File \param fragmentFile Fragment Shader File.
       glShader* loadfromMemory(const char* vertexMem, const char* geometryMem, const char* fragmentMem, bool bLink=true); //!< load vertex/geometry/fragment shader from memory. If you specify 0 for one of the shaders, the fixed function pipeline is used for that part.
       
       void      SetInputPrimitiveType(int nInputPrimitiveType);    //!< Set the input primitive type for the geometry shader \param nInputPrimitiveType Input Primitive Type, for example GL_TRIANGLES
       void      SetOutputPrimitiveType(int nOutputPrimitiveType);  //!< Set the output primitive type for the geometry shader \param nOutputPrimitiveType Output Primitive Type, for example GL_TRIANGLE_STRIP
       void      SetVerticesOut(int nVerticesOut);                  //!< Set the maximal number of vertices the geometry shader can output \param nVerticesOut Maximal number of output vertices. It is possible to output less vertices!
  
       bool      freeshader(glShader* o); //!< Remove the shader and free the memory occupied by this shader.

   private:
       std::vector<glShader*>  _shaderObjectList; 
       int                     _nInputPrimitiveType;
       int                     _nOutputPrimitiveType;
       int                     _nVerticesOut;
   };

#endif // A_GLSL_H

